/* SPU Parallel Elliptic Curve Library - Provides Elliptic Curve Cryptographic
 * functions to the Synergistic Processing Unit target platform
 *
 * Copyright (C) 2008  Michael Annichiarico (mike.gcdeveloper@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include"spec.h"
#include<string.h>
#include<spu_intrinsics.h>
#include<spu_mfcio.h>

#define spec_printpoint(p) \
	printf("point " #p ":\n"); spec_point_print(p)

void timing_test();
unsigned int spudec;
#define startDec() spu_write_decrementer(0xffffffff);spudec=spu_read_decrementer();
#define stopDec() spudec-=spu_read_decrementer();
#define printDec(cnt) printf("decdelta = %09d for %05d = %09d iterations\n",spudec,cnt,spudec/cnt);

int main(int argc, char** argv) {
	ECPOINT p=nistb233grp.G;
	ECPOINT q=nistb233grp.G;
	ECPOINT s=nistb233grp.G;
	ECPOINT negG=nistb233grp.G;
	ECPOINT Q;
	FIELD2M n = nistb233grp.n;
	FIELD2M nminus1;
	FIELD2M k = field2m_init_ull4(0,0,0,2);
	int i;

	printf("calculating c = b^2^(m-1) ... square b (m-1) times ... test: square it once more to get back to b\n");
	nistb233grp.c = nistb233grp.b;
	printf("b: "); poly_print(&nistb233grp.c); printf("\n");
	for(i=0;i<233-1;++i) poly_mod_sqr(&nistb233grp.c,&nistb233grp.c);
	printf("c: "); poly_print(&nistb233grp.c); printf("\n");
	startDec();
	poly_mod_sqr(&nistb233grp.c,&nistb233grp.c);
	stopDec();
	printf("TIMING: poly_mod_sqr: "); printDec(1);
	printf("c^2 == b?: "); poly_print(&nistb233grp.c); printf("\n");

	startDec();
	poly_mod_mult(&nminus1,&nistb233grp.c,&nistb233grp.b);
	stopDec();
	printf("TIMING: poly_mod_mult: "); printDec(1);
	startDec();
	poly_inv_mod(&nminus1,&nistb233grp.b);
	stopDec();
	printf("TIMING: poly_inv_mod: "); printDec(1);
	startDec();
	poly_add(&nminus1,&nistb233grp.b,&nistb233grp.c);
	stopDec();
	printf("TIMING: poly_add: "); printDec(1);

	poly_add(&nminus1,&n,&poly_one); //n=n-1 since n is odd and xoring removes the bottom bit

	printf("G:\n");
	spec_printpoint(&p);
	poly_add(&negG.y,&negG.y,&negG.x);
	printf("-G:\n");
	spec_printpoint(&negG);

	printf("working with non-Montgomery, but with LD projective\n");
	printf("p=dbl G, q=2*G ... should match\n");
	spec_point_LDdbl(&nistb233grp,&p,&p);
	spec_point_LD2A(&nistb233grp,&p,&p);
	spec_printpoint(&p);
	
	spec_point_LDkP(&nistb233grp,&q,&k,&s);
	spec_printpoint(&q);
	if(memcmp(&p,&q,sizeof(p))==0) {
		printf("p==q PASS\n");
	} else {
		printf("p!=q FAIL\n");
	}
	printf("trying mont LD but also doing y's\n");
	spec_point_MkP(&nistb233grp,&p,&k,&s);
	printf(" MkP: 2G:\n"); spec_printpoint(&p);
	if(memcmp(&p,&q,sizeof(p))==0) {
		printf("p==q PASS\n");
	} else {
		printf("p!=q FAIL\n");
	}

	printf("ok comparing LDkP montversion and LDkP binary version\n");
	for(i=0;i<5;++i) {
	printf("k: "); poly_print(&k);
	//calc using binary
	spec_point_LDkP(&nistb233grp,&q,&k,&s);
	printf(" LDkP: kG:\n"); spec_printpoint(&q);
	//calc using mont
	spec_point_MkP(&nistb233grp,&p,&k,&s);
	printf(" MkP: kG:\n"); spec_printpoint(&p);
	if(memcmp(&p,&q,sizeof(p))==0) {
		printf("p==q PASS\n");
	} else {
		printf("p!=q FAIL\n");
	}
	//update k.
	k.e[1]++;
	}

	printf("since n is order of subgroup from G, then nG==inf? or (n-1)G==-G?\n");
	spec_point_LDkP(&nistb233grp,&Q,&nminus1,&nistb233grp.G);
	printf("Q=(n-1)*G:\n");
	spec_printpoint(&Q);
	if(spec_point_cmpeq(&Q,&negG)) {
		printf("(n-1)*G == -G PASS\n");
	} else {
		printf("(n-1)*G != -G FAIL\n");
	}

	printf("working with Montgomery, and LD projective\n");
	printf("since n is order of subgroup from G, then nG==inf? or (n-1)G==-G?\n");
	spec_point_MkP(&nistb233grp,&Q,&nminus1,&nistb233grp.G);
	printf("Q=(n-1)*G:\n");
	spec_printpoint(&Q);
	if(spec_point_cmpeq(&Q,&negG)) {
		printf("(n-1)*G == -G PASS\n");
	} else {
		printf("(n-1)*G != -G FAIL\n");
	}
	p=nistb233grp.G;
	q=nistb233grp.G;
	s=nistb233grp.G;
	printf("since n is order of subgroup from G, then nG==G or (n-1)G==1?\n");
	//poly_add(&n,&n,&poly_one);
	spec_point_MkP(&nistb233grp,&Q,&n,&nistb233grp.G);
	printf("Q:\n");
	spec_printpoint(&Q);

	printf("p=dbl G, q=2*G ... should match\n");
	spec_point_Mdbl(&nistb233grp,&p);
	spec_point_Mxy(&nistb233grp,&p,&p,&p);
	p.y=p.z; p.z=poly_one;
	spec_printpoint(&p);
	
	spec_point_MkP(&nistb233grp,&q,&k,&s);
	spec_printpoint(&q);
	if(memcmp(&p,&q,sizeof(p))==0) {
		printf("p==q PASS\n");
	} else {
		printf("p!=q FAIL\n");
	}

/*
	printf("p=2p = 2(2G) = 4G...  s=G+G+G+G = 4G, should match\n");
	spec_point_Mdbl(&nistb233grp,&p);
	spec_printpoint(&p);
	spec_point_Madd(&nistb233grp,&s.x,&s,&nistb233grp.G);
	spec_point_Madd(&nistb233grp,&s.x,&s,&nistb233grp.G);
	spec_point_Madd(&nistb233grp,&s.x,&s,&nistb233grp.G);
	spec_printpoint(&s);
	if(memcmp(&p,&s,sizeof(p))==0) {
		printf("p==s PASS\n");
	} else {
		printf("p!=s FAIL\n");
	}
*/

	printf("Testing simul point multiply\n");
	{
		//FIELD2M kbe=field2m_init_ull4be(5555555555,5555555555555555,5555555555555555,5555555555555555);
		//FIELD2M lbe=field2m_init_ull4be(AAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);
		FIELD2M kbe=field2m_init_ull4be(0,0,0,A2);
		FIELD2M lbe=field2m_init_ull4be(0,0,0,53);
		FIELD2M Bbe=field2m_init_ull4be(AAAAAAAAAA,deadbeafdeadbeaf,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);
		FIELD2M Abe=field2m_init_ull4be(AAAAAAAAAA,beafdeadbeafdead,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);
		ECPOINT P,Q,R1,R2;
		spec_point_MkP_be(&nistb233grp,&P,&Abe,&nistb233grp.G);
		spec_point_MkP_be(&nistb233grp,&Q,&Bbe,&nistb233grp.G);
		P.z=poly_one;
		Q.z=poly_one;

		spec_point_MkP_be(&nistb233grp,&R1,&kbe,&P);
		printf("kP:\n");spec_point_print(&R1);
		spec_point_MkP_be(&nistb233grp,&R2,&lbe,&Q);
		printf("lQ:\n");spec_point_print(&R2);
		if(spec_point_cmpeq(&R1,&spec_point_Ainf)) R1=spec_point_LDinf; else
		R1.z=poly_one;
		spec_point_LDadd(&nistb233grp,&R2,&R1,&R2);
		printf("kP+lQ (LD):\n");spec_point_print(&R2);
		spec_point_LD2A(&nistb233grp,&R1,&R2);
		//normal slow way is NOW IN R1
		//spec_point_simulMkP_be(&nistb233grp,&R2,&kbe,&P,&lbe,&Q);
		spec_point_simulMkP_be(&nistb233grp,&R2,&lbe,&Q,&kbe,&P);

		printf("spec_point_simulMkP_be: R1:\n");
		spec_point_print(&R1);
		printf("spec_point_simulMkP_be: R2:\n");
		spec_point_print(&R2);

		if(poly_cmpeq(&R1.x,&R2.x)) {
			printf("spec_point_simulMkP_be SUCCESS!\n");
		} else {
			printf("spec_point_simulMkP_be Failed!!!\n");
		}
	}

	printf("Testing ecdsa_sign and ecdsa verify\n");
	{
		int res;
		FIELD2M r,s;
		ECPOINT Q;
		//FIELD2M d=field2m_init_ull4be(0,0,0,A);
		FIELD2M d=field2m_init_ull4be(5555555555,5555555555555555,5555555555555555,5555555555555555);
		FIELD2M dle=field2m_init_ull4(0,0,0,A);
		FIELD2M e=field2m_init_ull4be(0,DEAD,C01D,BEEF);
		FIELD2M kkle=field2m_init_ull4(AAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);
		FIELD2M kkbe=field2m_init_ull4be(AAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);
		//FIELD2M kkbe=field2m_init_ull4be(0,1,2,3);

		printf("Generating Public Key\n");
		//spec_point_MkP(&nistb233grp,&Q,&dle,&nistb233grp.G);
		spec_point_MkP_be(&nistb233grp,&Q,&d,&nistb233grp.G);
		printf("Signing...\n");
		startDec();
		res=spec_ecdsa_sign(&nistb233grp,&kkbe,&d,&e,&r,&s);
		stopDec();
		printf("TIMING: spec_ecdsa_sign: "); printDec(1);
		printf("result: %d\n",res);
		if(res!=0) {
			printf("SIGN FAILED\n");
		}else{
			printf("Verifying...\n");
			startDec();
			res=spec_ecdsa_verify(&nistb233grp,&Q,&e,&r,&s);
			stopDec();
			printf("TIMING: spec_ecdsa_verify: "); printDec(1);
			printf("result: %d\n",res);
			if(res!=1) printf("VERIFY FAILED!\n"); else printf("VERIFY SUCCESS!\n");
			printf("now alter r just to make sure");
			r=poly_one;
			e=poly_one;
			res=spec_ecdsa_verify(&nistb233grp,&Q,&e,&r,&s);
			printf("result: %d\n",res);
			if(res==0) printf("VERIFY CORRECTLY failed!\n"); else printf("VERIFY succeeded INCORRECTLY\n");
			
		}
		

	}
	
	timing_test();
	return(0);
}

void timing_test() {
		ECPOINT Q,P,R,Qld,Pld,Rld;
		FIELD2M x=field2m_init_ull4be(0,0,0,A);
		FIELD2M y=field2m_init_ull4(0,0,0,A);
		FIELD2M kkbe=field2m_init_ull4be(0,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);
		FIELD2M kkle=field2m_init_ull4(0,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAA);

	//time MkP_be
	startDec();
	spec_point_MkP_be(&nistb233grp,&Q,&kkbe,&nistb233grp.G);
	stopDec();
	printf("TIMING: MkP_be: "); printDec(1);
	//end time MkP_be

	//time LDkP
	startDec();
	spec_point_LDkP(&nistb233grp,&Q,&kkle,&nistb233grp.G);
	stopDec();
	printf("TIMING: LDkP: "); printDec(1);
	//end time LDkP
	
	//time Mdbl
	spec_point_LDdbl(&nistb233grp,&P,&nistb233grp.G);
	startDec();
	spec_point_Mdbl(&nistb233grp,&P);
	stopDec();
	printf("TIMING: Mdbl: "); printDec(1);
	//end time Mdbl
	
	//time LDdbl
	spec_point_LDdbl(&nistb233grp,&P,&nistb233grp.G);
	spec_point_LDdbl(&nistb233grp,&Q,&nistb233grp.G);
	startDec();
	spec_point_LDdbl(&nistb233grp,&P,&Q);
	stopDec();
	printf("TIMING: LDdbl: "); printDec(1);
	//end time LDdbl
	
	//time LDdbl
	spec_point_LDdbl(&nistb233grp,&P,&nistb233grp.G);
	spec_point_LDdbl(&nistb233grp,&Q,&nistb233grp.G);
	startDec();
	spec_point_MLDdbl(&nistb233grp,&P,&Q);
	stopDec();
	printf("TIMING: MLDdbl: "); printDec(1);
	//end time LDdbl

	//time Mxy
	startDec();
	spec_point_Mxy(&nistb233grp,&R,&Q,&P);
	stopDec();
	printf("TIMING: Mxy: "); printDec(1);
	//end time Mxy
	
	//time LD2A
	startDec();
	spec_point_LD2A(&nistb233grp,&R,&P);
	stopDec();
	printf("TIMING: LD2A: "); printDec(1);
	//end time LD2A 

	//time Madd
	spec_point_Mdbl(&nistb233grp,&R);
	spec_point_Mdbl(&nistb233grp,&R);
	startDec();
	spec_point_Madd(&nistb233grp,&Q.x,&P,&R);
	stopDec();
	printf("TIMING: Madd: "); printDec(1);
	//end time Madd
	
	//time LDadd
	spec_point_LDdbl(&nistb233grp,&P,&R);
	spec_point_LDdbl(&nistb233grp,&R,&P);
	startDec();
	spec_point_LDadd(&nistb233grp,&Q,&P,&R);
	stopDec();
	printf("TIMING: LDadd: "); printDec(1);
	//end time Madd
	
	//time MLDadd
	spec_point_LDdbl(&nistb233grp,&P,&R);
	spec_point_LDdbl(&nistb233grp,&R,&P);
	startDec();
	spec_point_MLDadd(&nistb233grp,&Q.x,&P,&R);
	stopDec();
	printf("TIMING: MLDadd: "); printDec(1);
	//end time Madd

	startDec();
	sleep(2);
	stopDec();
	printf("TIMING: sleep(2): "); printDec(2);
}

